/**
  Authors:
  	Wei Li (oldrev@gmail.com)
  
  Copyright:	
  	Copyright (C) 2007 Wei Li. All rights reserved.
  
  License:	
  	BSD
  
  Date: 
  	June 1 2007
  */

module dotmars.text.encoding;

import dotmars.base.stdtypes;
import dotmars.base.convert;
import dotmars.text.utf;



//The native encoding of D is UTF-8

abstract class Encoder
{
	public void reset() {
		//do nothing
	}

	public abstract size_t encode(string chars, Sink!(ubyte) sink, bool flush);

	public ubyte[] encode(string str, bool flush)
	in{
		assert(str !is null);
	}
	body {
		ubyte[] result;
		Sink!(ubyte) sink = (ubyte b){result ~= b;} ;
		encode(str, sink, flush);
		return result;
	}
}

abstract class Decoder
{
	public void reset() {
	}

	public abstract size_t decode(ubyte[] bytes, Sink!(char) sink, bool flush);

	public string decode(ubyte[] bytes, bool flush)
	in {
		assert(bytes !is null);
	}
	body{
		string result;
		Sink!(char) sink = (char c){result ~= c;} ;
		decode(bytes, sink, flush);
		return result;
	}
}

///////////////////////////////////////////////////////////////////////////////

abstract class Encoding : ICloneable
{
	protected final class InternalEncoder : Encoder
	{
		private Encoding m_encoding;

		public alias Encoder.encode encode;

		public this(Encoding enc) {
			m_encoding = enc;
		}

		public override size_t encode(string chars, Sink!(ubyte) sink, bool flush) {
			return m_encoding.encode(chars, sink);
		}
	}

	protected final class InternalDecoder : Decoder
	{
		private Encoding m_encoding;

		public alias Decoder.decode decode;

		public this(Encoding enc) {
			m_encoding = enc;
		}

		public override size_t decode(ubyte[] bytes, Sink!(char) sink, bool flush) {
			return m_encoding.decode(bytes, sink);
		}
	}

	public this()
	{
	}

	public static ubyte[] convert(Encoding src, Encoding dest, ubyte[] srcBytes)
	in {
		assert(src !is null);
		assert(dest !is null);
		assert(srcBytes !is null);
	}
	body {
		return dest.encode(src.decode(srcBytes));
	}

	public ubyte[] encode(string str)
	in{
		assert(str !is null);
	}
	body {
		ubyte[] result;
		Sink!(ubyte) sink = (ubyte b){result ~= b;} ;
		encode(str, sink );
		return result;
	}

	public string decode(ubyte[] bytes)
	in {
		assert(bytes !is null);
	}
	body{
		string result;
		Sink!(char) sink = (char c){result ~= c;} ;
		decode(bytes, sink);
		return result;
	}

	public Encoder getEncoder() {
		return new InternalEncoder(this);
	}

	public Decoder getDecoder() {
		return new InternalDecoder(this);
	}

	// 必须 override

	public abstract Encoding clone();

	public abstract string encodingName();

	public abstract bool isSingleByte();

	public abstract ubyte[] preamble();

	public abstract size_t getByteCount(string str);

	public abstract size_t getCharCount(ubyte[] bytes);

	public abstract size_t getMaxByteCount(size_t charCount);

	public abstract size_t getMaxCharCount(size_t byteCount);

	public abstract size_t decode(ubyte[] bytes, Sink!(char) sink);

	public abstract size_t encode(string chars, Sink!(ubyte) sink);
}


////////////////////////////////////////////////////////////////////////////////


package struct CharBuffer(T, uint MaxCharCount)
{
	static assert( MaxCharCount <= 6);
	alias T CharType;

	private struct State
	{
		char[MaxCharCount] buf = void;
		ubyte offset;
		ubyte left;
		ubyte inChar;
	}

	private State m_state;

	public size_t left() 
	{
		return m_state.left; 
	}

	public void reset() 
	{
		m_state.offset = 0;
		m_state.left = 0;
		m_state.inChar = 0;
	}

	public void putChar(CharType c)
	{
		m_state.buf[m_state.offset++] = c;
		if(m_state.left == 0 && m_state.inChar == 0) //开始
		{
			m_state.left = stride(c) - 1;
			m_state.inChar = 1;
		}
		else { //在UTF字符序列中
			m_state.left--;
		}
	}

	public dchar getDchar() 
	{
		assert(isFull());
		dchar d = void;
		toUtf32(m_state.buf[0..m_state.offset], d);
		return d;
	}

	private bool  isFull() 
	{
		return m_state.left == 0 && m_state.inChar;
	}
}
